const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  // 默认状态
  status = PENDING;
  value = undefined;
  reason = undefined;
  // 改为数组
  fulfillReactions = [];
  rejectReactions = [];

  static all(array) {
    const result = [];
    const index = 0;

    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        // 当所有任务执行完毕之后 resolve
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        if (current instanceof MyPromise) {
          current.then(
            (value) => addData(i, value),
            (reason) => {
              reject(reason);
            },
          );
        } else {
          addData(i, array[i]);
        }
      }
    });
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => resolve(value));
  }

  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    this.value = value;
    while (this.fulfillReactions.length) this.fulfillReactions.shift()(value);
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    while (this.rejectReactions.length) this.rejectReactions.shift()(reason);
  };

  then(fulfillReaction, rejectReaction) {
    // 没有传入则补一个
    fulfillReaction = fulfillReaction ? fulfillReaction : (value) => value;
    rejectReaction = rejectReaction
      ? rejectReaction
      : (reason) => {
          throw reason;
        };
    const newPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // 捕获 then 回调中的错误
          try {
            const cbReturn = fulfillReaction(this.value);
            resolvePromise(newPromise, cbReturn, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        // 使用同样的方法处理错误
        setTimeout(() => {
          try {
            const cbReturn = rejectReaction(this.reason);
            resolvePromise(newPromise, cbReturn, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 同样的方法处理异步成功的回调
        this.fulfillReactions.push(() => {
          setTimeout(() => {
            try {
              const cbReturn = fulfillReaction(this.value);
              resolvePromise(newPromise, cbReturn, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        // 同样的方法处理异步失败的回调
        this.rejectReactions.push(() => {
          setTimeout(() => {
            try {
              const cbReturn = rejectReaction(this.reason);
              resolvePromise(newPromise, cbReturn, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return newPromise;
  }

  finally(cb) {
    // 是一种不管是否成功都会调用的 then
    // 不管 callback 返回什么，都被放入 Promise
    // 这个 Promise 会被立刻执行掉，并且把值和错误传递下去
    this.then(
      (value) => {
        return MyPromise.resolve(cb()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(cb()).then(() => {
          throw reason;
        });
      },
    );
  }

  // 就是注册一个错误回调
  catch(rejectReaction) {
    return this.then(undefined, rejectReaction);
  }
}

function resolvePromise(newPromise, cbReturn, resolve, reject) {
  // 回调的返回值与 then 返回的对象全等
  if (newPromise === cbReturn) {
    reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
  }
  if (cbReturn instanceof MyPromise) {
    // 给返回的 Promise 注册回调
    // cbReturn.then(value => resolve(value), reason => reject(reason))
    cbReturn.then(resolve, reject);
  } else {
    resolve(cbReturn);
  }
}

export default MyPromise;
